#include "ustc_comdef.h"
#include "ustc_custom.h"
#include "ustc_api.h"
#include "ustc_usb_drv.h"
#include "ustc_usb_scsi.h"
#include "ustc_usb_bulk.h"
#include "ustc_usb_chap9.h"

extern USB_PATH_T *gp_usb_path;

USB_DEVICE_DESCRIPTOR_T usb_udisk_desc =
{
	sizeof(USB_DEVICE_DESCRIPTOR_T),
	USB_DEVICE_DESCRIPTOR_TYPE,
	0x0110,
    0,
	0,
	0,
	EP0_PACKET_SIZE,
    0x171f,//VID
   	0x0889,//Mass PID
   	0x0100,
    STR_INDEX_MANUFACTURER,
	STR_INDEX_PRODUCT,
	STR_INDEX_SERIALNUMBER,
    1
};

const USTC_U8 usb_udisk_ConfigDescr[] = 
{
	//configuration description
	0x09, 		//bLength
	0x02,		//bDescriptorType, configureuration
	0x20,		//wTotalLength, 0x0020
	0x00,
	0x01,		//bNumInterfaces
	0x01,		//bConfigurationValue
	0x00,		//iConfiguration
	0x40,		//bmAttributes,0xc0:self power
	0x32,    	//MaxPower

	//interface description
	0x09,		//bLength
	0x04,		//bDescriptorType
	0x00,		//bInterfaceNumber
	0x00,		//bAlternateSetting
	0x02,		//bNumEndpoints
	0x08,		//bInterfaceClass, mass storage class
	0x06,		//bInterfaceSubClass, SCSI class
	0x50,		//bInterfaceProtocol, bulk only
	0x00,		//iInterface

	//endpoint description
	0x07,		//bLength
	0x05,		//bDescriptorType
	0x81,		//bEndpointAddress,  in
	0x02,		//bmAttributes,bulk
	0x40,		//wMaxPacketSize
	0x00,		//
	0x00,		//bInterval

	//endpoint description
	0x07,		//bLength
	0x05,		//bDescriptorType
	0x01,		//bEndpointAddress, out
	0x02,		//bmAttributes,bulk
	0x40,		//wMaxPacketSize
	0x00,		//
	0x00		//bInterval
	
};

const USTC_U8 usb_pccam_ConfigDescr[] = 
{
	//configuration description
	0x09, 		//bLength
	0x02,		//bDescriptorType, configureuration
	0x20,		//wTotalLength, 0x0020
	0x00,
	0x01,		//bNumInterfaces
	0x01,		//bConfigurationValue
	0x00,		//iConfiguration
	0x40,		//bmAttributes,0xc0:self power
	0x32,    		//MaxPower

	//interface description
	0x09,		//bLength
	0x04,		//bDescriptorType
	0x00,		//bInterfaceNumber
	0x00,		//bAlternateSetting
	0x02,		//bNumEndpoints
	0xff,		//bInterfaceClass, Vender-specific class
	0xa0,		//bInterfaceSubClass, 
	0xb0,		//bInterfaceProtocol, 
	0x00,		//iInterface

	//endpoint description
	0x07,		//bLength
	0x05,		//bDescriptorType
	0x81,		//bEndpointAddress,  in
	0x02,		//bmAttributes,bulk
	0x40,		//wMaxPacketSize
	0x00,		//
	0x00,		//bInterval

	//endpoint description
	0x07,		//bLength
	0x05,		//bDescriptorType
	0x01,		//bEndpointAddress, out
	0x02,		//bmAttributes,bulk
	0x40,		//wMaxPacketSize
	0x00,		//
	0x00		//bInterval
	
};

#if 0
const USTC_U8 vserial_ConfigDescr_fs[] = 
{
	//configuration description
	0x09, 		//bLength
	0x02,//USB_CONFIGURATION_DESCRIPTOR_TYPE,//0x02,//bDescriptorType, configureuration
	0x43,		//wTotalLength, 0x0020
	0x00,
	0x02,		//bNumInterfaces
	0x01,		//bConfigurationValue
	0x00,		//iConfiguration
	0xC0,		//bmAttributes,0xc0:self power
	0x32,    	//MaxPower
	
	//interface description
	0x09,		//bLength
	0x04,//USB_INTERFACE_DESCRIPTOR_TYPE,//0x04,//bDescriptorType
	0x00,		//bInterfaceNumber
	0x00,		//bAlternateSetting
	0x01,		//bNumEndpoints
	0x02,//USB_CLASS_CDC_CLASS_DEVICE,//USB_CLASS_CODE_MASSSTORAGE_CLASS_DEVICE,//0x08,//bInterfaceClass, mass storage class
	0x02,//USB_SUBCLASS_ABSTRACT_CONTROL_MODEL,//USB_SUBCLASS_CODE_SCSI,//0x06,	//bInterfaceSubClass, SCSI class
	0x01,//USB_PROTOCOL_NO_SPECIFIC,//USB_PROTOCOL_CODE_BULK,//0x50,	//bInterfaceProtocol, bulk only
	0x00,		//iInterface
#if 1
//   AT89C5131 data
//    { 0x05, 0x24, 0x00, 0x10, 0x01, 0x05, 0x24, 0x01, 0x03, 0x01, 0x04, 0x24, 0x02, 0x06,0x05, 0x24, 0x06, 0x00, 0x01 },
	0x05,0x24,0x0,0x01,0x01,
	0x05,0x24,0x01,0x03,0x01,
	0x05,0x24,0x06,0x00,0x01,
	0x04,0x24,0x02,0x02,
#endif
	//endpoint description
	0x07,		//bLength
	0x05,//USB_ENDPOINT_DESCRIPTOR_TYPE,//0x05,//bDescriptorType
	0x84,		//bEndpointAddress, IN
	0x03,//USB_ENDPOINT_TYPE_INTERRUPT,//0x02,//bmAttributes,bulk
	0x40,	//wMaxPacketSize,Low byte
	0x00,	//wMaxPacketSize,high byte	
	0x00,	//bInterval

	//interface description
	0x09,		//bLength
	0x04,//USB_INTERFACE_DESCRIPTOR_TYPE,//0x04,//bDescriptorType
	0x01,		//bInterfaceNumber
	0x00,		//bAlternateSetting
	0x02,		//bNumEndpoints
	0x0A,//USB_CLASS_CODE_MASSSTORAGE_CLASS_DEVICE,//0x08,//bInterfaceClass, mass storage class
	0x00,//USB_SUBCLASS_CODE_SCSI,//0x06,	//bInterfaceSubClass, SCSI class
	0x00,//USB_PROTOCOL_NO_SPECIFIC,//USB_PROTOCOL_CODE_BULK,//0x50,	//bInterfaceProtocol, bulk only
	0x00,		//iInterface
	//endpoint description
	0x07,		//bLength
	0x05,//USB_ENDPOINT_DESCRIPTOR_TYPE,//0x05,//bDescriptorType
	0x83,		//bEndpointAddress,  in
	0x02,//USB_ENDPOINT_TYPE_BULK,//0x02,//bmAttributes,bulk
	0x40,//0x40,		//wMaxPacketSize
	0x00,		//
	0x00,		//bInterval

	//endpoint description
	0x07,		//bLength
	0x05,//USB_ENDPOINT_DESCRIPTOR_TYPE,//0x05,//bDescriptorType
	0x02,		//bEndpointAddress, out
	0x02,//USB_ENDPOINT_TYPE_BULK,//0x02,//bmAttributes,bulk
	0x40,//0x40,//wMaxPacketSize
	0x00,		//
	0x00		//bInterval
	
};
#endif

const USB_STRING_LANGUAGE_DESCRIPTOR_T  strLanguage =
{
	sizeof(USB_STRING_LANGUAGE_DESCRIPTOR_T),
	USB_STRING_DESCRIPTOR_TYPE,
	0x0409
};

#ifndef ENABLE_DESCRIPTIONSTRING_USB
const USB_STRING_SERIALNUMBER_DESCRIPTOR_T  strSerialNum =
#else
USB_STRING_SERIALNUMBER_DESCRIPTOR_T  strSerialNum =
#endif
{
	sizeof(strSerialNum),
	USB_STRING_DESCRIPTOR_TYPE,
   	{
	'0',0,
	'0',0,
	'0',0,
	'0',0,

	'0',0,
	'0',0,
	'0',0,
	'0',0,

	'0',0,
	'0',0,
	'0',0,
	'0',0}
};


//send data to host via EP0(index 1,control pipe)
//buff: must be global,so INT handler,DCP FSM can handle send data more times
/******************************************************************************
 * FUNCTION:    chap9_control_transmit
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              unsigned char *buff
 *              unsigned short len
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_control_transmit(unsigned char *buff,unsigned short len)
{
	gp_usb_path->control_data.wCount =0;
	if (gp_usb_path->control_data.wLength > len)//host request length larger than actual
		gp_usb_path->control_data.wLength = len;
	gp_usb_path->control_data.pData = buff;
	if (gp_usb_path->control_data.wLength >= EP0_PACKET_SIZE)
	{	
		ustc_memcpy(gp_usb_path->ep_buff_tx,buff,EP0_PACKET_SIZE);
		gp_usb_path->ep_tx_len = EP0_PACKET_SIZE;
		usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len);
		gp_usb_path->control_data.wCount += EP0_PACKET_SIZE;
		gp_usb_path->control_state = USB_TRANSMIT;
	}
	else
	{
		ustc_memcpy(gp_usb_path->ep_buff_tx,buff,gp_usb_path->control_data.wLength);
		gp_usb_path->ep_tx_len  = gp_usb_path->control_data.wLength;
		usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len);
		gp_usb_path->control_data.wCount +=  gp_usb_path->control_data.wLength;
		gp_usb_path->control_state = USB_IDLE;
	}	
	
}


//
// USB standard device requests
//
/******************************************************************************
 * FUNCTION:    chap9_get_descriptor
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_get_descriptor(void)
{
	unsigned char* pbuf;
	
	//???,high 8 bits or low,check spec
	unsigned char bDescriptor = gp_usb_path->control_data.DeviceRequest.wValue >>8;
	if (bDescriptor == USB_DEVICE_DESCRIPTOR_TYPE)
	{
		chap9_control_transmit((unsigned char*)&usb_udisk_desc,sizeof(USB_DEVICE_DESCRIPTOR_T));
	}
	else if (bDescriptor == USB_CONFIGURATION_DESCRIPTOR_TYPE)
	{
		if (gp_usb_path->control_data.DeviceRequest.wLength > CONFIG_DESCRIPTOR_LENGTH)
			gp_usb_path->control_data.DeviceRequest.wLength = CONFIG_DESCRIPTOR_LENGTH;

		chap9_control_transmit((unsigned char*)&usb_udisk_ConfigDescr,gp_usb_path->control_data.DeviceRequest.wLength);
	}
	else if (bDescriptor == USB_STRING_DESCRIPTOR_TYPE)
	{
		switch (gp_usb_path->control_data.DeviceRequest.wValue & 0xf)
		{
			case STR_INDEX_LANGUAGE:
				pbuf = (unsigned char*)&strLanguage;
				chap9_control_transmit((unsigned char*)&strLanguage,sizeof(USB_STRING_LANGUAGE_DESCRIPTOR_T));

				break;

			case STR_INDEX_MANUFACTURER:
				ustc_memcpy(gp_usb_path->usb_cache+2,gp_usb_path->p_para_usb->usb_vendor,gp_usb_path->p_para_usb->usb_vendor_size);
				gp_usb_path->usb_cache[0] = 2+gp_usb_path->p_para_usb->usb_vendor_size;
				gp_usb_path->usb_cache[1] = USB_STRING_DESCRIPTOR_TYPE;

				chap9_control_transmit((unsigned char*)gp_usb_path->usb_cache,gp_usb_path->usb_cache[0]);

				break;

			case STR_INDEX_PRODUCT:
				ustc_memcpy(gp_usb_path->usb_cache+2,gp_usb_path->p_para_usb->usb_vendor,gp_usb_path->p_para_usb->usb_vendor_size);
				ustc_memcpy(gp_usb_path->usb_cache+2+gp_usb_path->p_para_usb->usb_vendor_size,gp_usb_path->p_para_usb->usb_product,gp_usb_path->p_para_usb->usb_product_size);
				gp_usb_path->usb_cache[0] = 2+gp_usb_path->p_para_usb->usb_vendor_size+gp_usb_path->p_para_usb->usb_product_size;
				gp_usb_path->usb_cache[1] = USB_STRING_DESCRIPTOR_TYPE;

				chap9_control_transmit((unsigned char*)gp_usb_path->usb_cache,gp_usb_path->usb_cache[0]);

				break;

			case STR_INDEX_SERIALNUMBER:
				
				chap9_control_transmit((unsigned char*)&strSerialNum,sizeof(USB_STRING_SERIALNUMBER_DESCRIPTOR_T));
										
				break;

			default:
				usb_stall_ep(0,PSTALL);
				break;	
		}
	}
	else
		usb_stall_ep(0,PSTALL);
}

/******************************************************************************
 * FUNCTION:    chap9_set_configuration
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_set_configuration(void)
{
	if (gp_usb_path->control_data.DeviceRequest.wValue ==0)//enter unconfig status,disable all EP except 0
	{
		usb_active_ep(0,0);
		usb_active_ep(2,0);
		usb_active_ep(3,0);
		//set configuration value
		usb_set_cfg(0);
		gp_usb_path->configuration =0;
		//set usb status: in configuation...
		gp_usb_path->usb_status = USB_IN_CONFIG;
		
	}
	else if (gp_usb_path->control_data.DeviceRequest.wValue ==1)//enter config status,enable all EP
	{
		//usb_active_ep(0,ACTIVE);
		usb_active_ep(2,ACTIVE);
		usb_active_ep(3,ACTIVE);
		#if 0
		//deric dummy endpoint
		usb_active_ep(4,ACTIVE));	
		#endif
		
		//set configuration value
		usb_set_cfg(1);
		gp_usb_path->configuration = 1;
		//set usb status:start bulk only transfer...
		gp_usb_path->usb_status = USB_TRANSFER_DATA;

		//prepare receive from endpoint 2(bulk out)
		usb_read_ep(1,NULL);
	}
	else
		usb_stall_ep(0,PSTALL);
}



/******************************************************************************
 * FUNCTION:    chap9_get_status
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_get_status(void)
{
	unsigned char endp,txdata[2];
	unsigned char bRecipient = gp_usb_path->control_data.DeviceRequest.bRequestType & USB_RECIPIENT;
	unsigned char c;
	
	switch (bRecipient)
	{
		case USB_RECIPIENT_DEVICE:
			txdata[0]=1;//self power,no remote wakeup
			txdata[1]=0;
			ustc_memcpy(gp_usb_path->ep_buff_tx,txdata,2);
			gp_usb_path->ep_tx_len = 2;
			usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len);
			break;
		case USB_RECIPIENT_INTERFACE:
			txdata[0]=0;
			txdata[1]=0;
			ustc_memcpy(gp_usb_path->ep_buff_tx,txdata,2);
			gp_usb_path->ep_tx_len = 2;
			usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len);
			break;
		case USB_RECIPIENT_ENDPOINT:
			endp = (unsigned char)(gp_usb_path->control_data.DeviceRequest.wIndex & MAX_ENDPOINTS);
			if (gp_usb_path->control_data.DeviceRequest.wIndex & USB_ENDPOINT_DIRECTION_MASK)
				c=usb_get_ep_status(endp*2+1);
			else
				c = usb_get_ep_status(endp*2);
			if (c & PSTALL)
				txdata[0] =1;
			else
				txdata[0]=0;
			txdata[1] = 0;
			ustc_memcpy(gp_usb_path->ep_buff_tx,txdata,2);
			gp_usb_path->ep_tx_len = 2;
			usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len);
			break;
		default:
			usb_stall_ep(0,PSTALL);
			break;
	}	
}

/******************************************************************************
 * FUNCTION:    chap9_get_configuration
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_get_configuration(void)
{
	unsigned char c = gp_usb_path->configuration;
	gp_usb_path->ep_buff_tx[0] = c;
	gp_usb_path->ep_tx_len = 1;
	usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len );
}

/******************************************************************************
 * FUNCTION:    chap9_set_interface
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_set_interface(void)
{
	if (gp_usb_path->control_data.DeviceRequest.wValue == 0 && gp_usb_path->control_data.DeviceRequest.wIndex == 0)
	{
		//send a zero length ACK,ep1(dcp out)
	}
	else
	{
		//stall EP0
		usb_stall_ep(0,PSTALL);
	}		   
}
/******************************************************************************
 * FUNCTION:    chap9_get_interface
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_get_interface(void)
{
	unsigned char txdata = 0;//only one interface
	gp_usb_path->ep_buff_tx[0] = txdata;
	gp_usb_path->ep_tx_len = 1;
	usb_write_ep(0,gp_usb_path->ep_buff_tx,gp_usb_path->ep_tx_len);
}

/******************************************************************************
 * FUNCTION:    chap9_set_feature
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_set_feature(void)
{
}

/******************************************************************************
 * FUNCTION:    chap9_clear_feature
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_clear_feature(void)
{
}

/******************************************************************************
 * FUNCTION:    chap9_set_address
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_set_address(void)
{

}


/*mass storage class request*/
/******************************************************************************
 * FUNCTION:    chap9_class_get_max_lun
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_class_get_max_lun(void)
{
	USTC_U8	 c =0;
	int i;

	//guosheng
	if(g_ustc_usb_contorl.sys_startmode==0)
	{
		for (i=0;i<4;i++)
			c += gp_usb_path->p_para_usb->udisk_disk_num[i];
		if (c>0)
			c-=1;
	}

	chap9_control_transmit(&c,1);
}

/*vendor request*/
/******************************************************************************
 * FUNCTION:    chap9_vendor_cmd_process
 *
 * DESCRIPTION: 
 *              
 * Inputs:      
 *              None
 * Outputs:     
 *              
 * Return:      
 *              
 * Notes:       
 *              
 ******************************************************************************/
void chap9_vendor_cmd_process(void)
{
}

